/*
* ExperienceMod - Bukkit server plugin for modifying the experience system in Minecraft.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.xp.rewards.xp;
import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.economy.EconomyResponse;
import org.apache.commons.lang.NullArgumentException;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import com.comphenix.xp.Configuration;
import com.comphenix.xp.Debugger;
import com.comphenix.xp.rewards.ItemRewardListener;
import com.comphenix.xp.rewards.ResourceHolder;
import com.comphenix.xp.rewards.ResourcesParser;
import com.comphenix.xp.rewards.RewardService;
import com.comphenix.xp.rewards.RewardTypes;
/**
* Rewards players with currency.
*
* @author Kristian
*/
public class RewardEconomy implements RewardService {
private Economy economy;
private Debugger debugger;
private ItemRewardListener listener;
// Default values
private ItemStack defaultItem = new ItemStack(Material.SLIME_BALL);
private int defaultWorth = 1;
// Drop items instead of giving currency directly
private ItemStack economyItem;
private Integer economyWorth;
// A currency parser
private ResourcesParser parser = new CurrencyParser(null);
public RewardEconomy(Economy economy, Debugger debugger, ItemRewardListener listener) {
if (economy == null)
throw new IllegalArgumentException("Vault (Economy) was not found.");
if (listener == null)
throw new NullArgumentException("listener");
this.debugger = debugger;
this.listener = listener;
this.economy = economy;
}
// Yes, this is a bit of a hack. But it's only a constant overhead.
@Override
public boolean canReward(Player player, ResourceHolder resource) {
if (player == null)
throw new NullArgumentException("player");
if (!(resource instanceof CurrencyHolder))
throw new IllegalArgumentException("Can only award currency.");
// Dry run
if (!economyReward(player, resource.getAmount(), null)) {
// Oh, no. It didn't work.
return false;
}
// We'll revert the changes
if (!economyReward(player, -resource.getAmount(), null)) {
if (debugger != null)
debugger.printDebug(this, "Unable to revert economy reward/penalty.");
// PANIC
return false;
}
// Yes, rewarding the player worked.
return true;
}
@Override
public void reward(Player player, ResourceHolder resource) {
if (!(resource instanceof CurrencyHolder))
throw new IllegalArgumentException("Can only award currency.");
economyReward(player, resource.getAmount(), debugger);
}
// Internal method
public boolean economyReward(Player player, int amount, Debugger debugger) {
if (player == null)
throw new NullArgumentException("player");
String name = player.getName();
if (amount < 0) {
// Make sure amount is positive
amount = Math.abs(amount);
// Just attempt to withdraw
EconomyResponse response = economy.withdrawPlayer(name, amount);
// Error?
if (response != null && !response.transactionSuccess()) {
// Could this be because the player is broke?
if (isLoan(name, amount)) {
if (debugger != null)
debugger.printDebug(this, "Could not withdraw %d: Player %s is broke",
amount, name);
} else {
// General error
if (debugger != null)
debugger.printDebug(this, "Coult not withdraw %d from player %s: %s",
amount, name, response.errorMessage);
}
// Failure
return false;
}
} else {
// Deposit money (shouldn't really fail)
EconomyResponse response = economy.depositPlayer(name, amount);
// But we'll do this just in case
if (response != null && !response.transactionSuccess() && debugger != null) {
debugger.printDebug(this, "Could not deposit %d to player %s: %s",
amount, name, response.errorMessage);
return false;
}
}
// Everything went well
return true;
}
// Location is ignored.
@Override
public void reward(Player player, Location point, ResourceHolder resource) {
// See if we have to reward the player directly
if (economyItem == null || economyWorth == null || economyWorth < 1) {
if (debugger != null)
debugger.printDebug(this, "Cannot find economy settings. Reverting to direct currency.");
reward(player, resource);
} else {
reward(player.getWorld(), point, resource);
}
}
@Override
public void reward(World world, Location point, ResourceHolder resource) {
if (world == null)
throw new NullArgumentException("world");
if (point == null)
throw new NullArgumentException("point");
if (!(resource instanceof CurrencyHolder))
throw new IllegalArgumentException("Can only award currency.");
ItemStack stack = economyItem != null ? economyItem : defaultItem;
Integer worth = economyWorth != null ? economyWorth : defaultWorth;
// Support negative rewards
int amount = Math.abs(resource.getAmount());
int sign = resource.getAmount() < 0 ? -1 : 1;
// Make sure it's valid too
if (worth < 1) {
worth = defaultWorth;
}
stack.addUnsafeEnchantment(Enchantment.ARROW_DAMAGE, -1);
// Create the proper amount of items
for (; amount > 0; amount -= worth) {
int thisAmount = sign * Math.min(amount, worth);
ItemStack thisStack = stack.clone();
ItemMeta meta = thisStack.getItemMeta();
meta.setDisplayName(Integer.toString(thisAmount));
thisStack.setItemMeta(meta);
Item spawned = world.dropItemNaturally(point, thisStack);
listener.pinReward(spawned, thisAmount);
}
}
@Override
public ResourcesParser getResourcesParser(String[] namedParameters) {
return parser.withParameters(namedParameters);
}
/**
* Determines whether or not withdrawing given amount requires a loan.
* @param name - name of the player to withdraw from.
* @param withdraw - the amount of currency to withdraw.
* @return TRUE if this requires a loan (negative bank balance), FALSE otherwise.
*/
private boolean isLoan(String name, int withdraw) {
return economy.getBalance(name) < withdraw;
}
@Override
public RewardTypes getRewardType() {
return RewardTypes.ECONOMY;
}
@Override
public String getServiceName() {
return getRewardType().name();
}
public ItemStack getEconomyItem() {
return economyItem;
}
public void setEconomyItem(ItemStack economyItem) {
this.economyItem = economyItem;
}
public Integer getEconomyWorth() {
return economyWorth;
}
public void setEconomyWorth(Integer economyWorth) {
this.economyWorth = economyWorth;
}
/**
* Determine the amount of currency a given player has access too.
* @param player - the player to check.
* @return The amount of currency belonging to this player.
*/
public double getBalance(Player player) {
return economy.getBalance(player.getName());
}
@Override
public RewardService clone(Configuration config) {
RewardEconomy copy = new RewardEconomy(economy, debugger, listener);
copy.setEconomyItem(config.getEconomyDropItem());
copy.setEconomyWorth(config.getEconomyItemWorth());
copy.parser = parser;
return copy;
}
}